package xyz.thoughtset.viewer.executor.blocks.executor;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import xyz.thoughtset.viewer.common.exc.exceptions.ExecException;
import xyz.thoughtset.viewer.modules.step.entity.BaseBlockBody;
import xyz.thoughtset.viewer.modules.step.entity.BlockTypeEnum;
import xyz.thoughtset.viewer.executor.blocks.utlis.BlockArgsUtils;
import xyz.thoughtset.viewer.modules.ds.core.factory.ExecutorManager;
import xyz.thoughtset.viewer.executor.blocks.constants.NodeConstant;
import xyz.thoughtset.viewer.modules.step.entity.block.BlockBodyEle;
import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

@SuppressWarnings({"unchecked","SpringJavaInjectionPointsAutowiringInspection"})
public abstract class AbstractBlockExecutor<T extends BaseBlockBody> implements InitializingBean {
    protected BlockTypeEnum supportType;
    @Autowired
    protected ExecutorManager executorManager;
    @Autowired
    protected ObjectMapper objectMapper;
    @Autowired
    protected BlockExecutorManager blockExecutorManager;
    protected TypeReference DATA_TYPE = new TypeReference<List<Map<String, Object>>>() {};

    @Override
    public void afterPropertiesSet() throws Exception {
        if (supportType == null) {
            supportType = getSupportType();
        }
        ExecutorRegistry.register(supportType, this);
        this.init();
    }
    protected void init() {}

    BlockTypeEnum getSupportType(){
        if (supportType == null) {
            Type superClass = getClass().getGenericSuperclass();
            if (superClass instanceof ParameterizedType) {
                // 获取实际类型参数
                Type[] typeArgs = ((ParameterizedType) superClass).getActualTypeArguments();
                supportType = BlockTypeEnum.fromClass((Class<T>) typeArgs[0]);
            }
        }
        return supportType;
    }

    @SneakyThrows
    public Object executeQuery(BlockInfo block, ExpressionParser parser, Map<String, Object> params, StandardEvaluationContext context) throws ExecException {
        T body = null;
        try {
            body = (T) objectMapper.readValue(block.getDataStr(),supportType.getTypeClass());
        } catch (Exception e) {
            body = (T) supportType.getTypeClass().getConstructor().newInstance();
        }
        body.setBodyEles((List<BlockBodyEle>) block.getBodys());
        if (ObjectUtils.isEmpty(body.getBodyEles())
                && !BlockTypeEnum.EXECAI.equals(supportType)
                && !BlockTypeEnum.MCP.equals(supportType)
                && !BlockTypeEnum.TRANSPOSE.equals(supportType)) {
            return null;
        }
        if (ObjectUtils.isEmpty(params)) {
            params = new HashMap<>();
        }
        return doQuery(block, body, params,parser, context);
    }
    protected abstract Object doQuery(BlockInfo block, T body, Map<String, Object> params, ExpressionParser parser,StandardEvaluationContext context) throws ExecException;


    protected Object execNode(BlockBodyEle ele, Map params, ExpressionParser parser, StandardEvaluationContext context){
        Map tmpMap = BlockArgsUtils.filterParams(ele, parser, context);
        Object val = ele.eleWasFun()? blockExecutorManager.execBlocks(ele.getEleId(),tmpMap,parser):
                executorManager.execute(ele.getEleId(),tmpMap);
        if (ObjectUtils.isEmpty(val) || !StringUtils.hasText(ele.getValNum())) {
            return val;
        }
        if("N".equals(ele.getValNum())){
            if (!(val instanceof Collection<?> || val.getClass().isArray())) {
                return new ArrayList<>(Collections.singletonList(val));
            }
        } else {
            Long index = 0L;
            try {
                index = Long.parseLong(ele.getValNum());
            }catch (Exception e){}

            if (1L <= index) {
                if (val instanceof List) {
                    if (((Collection<?>) val).size() > index) {
                        val = ((Collection<?>) val).stream()
                                .limit(index).collect(Collectors.toList());
                    }
                } else if (val.getClass().isArray()) {
                    if (((Object[]) val).length > index) {
                        val = Arrays.copyOf((Object[]) val, Math.min(((Object[]) val).length, index.intValue()));
                    }
                } else {
                    val = new ArrayList<>(Collections.singletonList(val));
                }
            } else {
                if (val instanceof List) {
                    val = ((Collection<?>) val).stream()
                            .limit(1).collect(Collectors.toList()).get(0);
                } else if (val.getClass().isArray()) {
                    val = ((Object[]) val)[0];
                }
            }
        }
        return val;
    }



    protected void putItVal(int index, Map<String, Object> params,StandardEvaluationContext context,Object val){
        String key = NodeConstant.getDataNodeWithNum(index);
        params.put(key, val);
        context.setVariable(key, val);
    }

}
